﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Collections;
using devtm.Collections;
using devtm.Types;


namespace devtm.Types.LazyLoading
{

    [Serializable]
    public class LazyLoadingHelper : IDisposable
    {

        #region properties
        
        private AppDomain domain;
        private IGetTypes _types;
        private EventHandler<ErrorEventArgs> _errorEvent;
        public event EventHandler<ErrorEventArgs> ErrorsEvent;
        public static TableCollection<DirectoryInfo, string> Directories { get; set; }
        
        #endregion

        #region ctor / ~

        public LazyLoadingHelper(
            TableCollection<DirectoryInfo, string> directories, 
            EventHandler<ErrorEventArgs> errorEvent)
        {

            DirectoryInfo d = directories.FirstOrDefault();
            domain = CreateDomaine(d);
            Type t = typeof(ClsGetTypes);

            _types = (IGetTypes)domain.CreateInstanceFromAndUnwrap(t.Assembly.CodeBase,
                t.FullName,
                false,
                BindingFlags.CreateInstance,
                null,
                null,
                System.Globalization.CultureInfo.CurrentCulture,
                null,
                null);

            ClsGetTypes.Directories = directories;
            Directories = directories;
            this.ErrorsEvent += _errorEvent;
            
        }


        public ResolveAssembly Resolver 
        { 
            get
            {
                return _types.Resolver;
            }
        }

        /// <summary>
        /// Create a new Domaine
        /// </summary>
        /// <returns></returns>
        private AppDomain CreateDomaine(DirectoryInfo applicationBase)
        {
            AppDomainSetup ads = new AppDomainSetup();
            ads.ApplicationBase = applicationBase.FullName;
            ads.DisallowBindingRedirects = false;
            ads.DisallowCodeDownload = true;
            ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            System.Security.PermissionSet P = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
            AppDomain Ad2 = AppDomain.CreateDomain("AD #2", null, ads, P);
            return Ad2;
        }

        public void Dispose()
        {

            if (this.ErrorsEvent != null)
                this.ErrorsEvent -= _errorEvent;

            _types.Dispose();
            _types = null;
            AppDomain.Unload(domain);
            domain = null;
        }
        
        #endregion

        #region methods
        
        public IEnumerable<Type> GetTypes(Assembly assembly)
        {
            try
            {
                return _types.GetTypes(assembly).ToList();
            }
            catch (Exception ex)
            {
                if (ErrorsEvent != null)
                    ErrorsEvent(_types, new ErrorEventArgs(ex));

                throw new LazyException(ex.Message, ex);
            }
        }


        public Type GetType(string type)
        {
            try
            {
                return _types.GetType(type);
            }
            catch (Exception ex)
            {
                if (ErrorsEvent != null)
                    ErrorsEvent(_types, new ErrorEventArgs(ex));

                throw new LazyException(ex.Message, ex);
            }
        }


        /// <summary>
        /// execute 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="act">The act.</param>
        /// <returns></returns>
        public T GetObject<T>(Func<T> act)
        {
            try
            {
                return _types.GetObject<T>(act);
            }
            catch (Exception ex)
            {
                if (ErrorsEvent != null)
                    ErrorsEvent(_types, new ErrorEventArgs(ex));

                throw new LazyException(ex.Message, ex);
            }
        }

        /// <summary>
        /// execute 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="act">The act.</param>
        /// <returns></returns>
        public void ExecuteObject(Action act)
        {
            try
            {
                _types.ExecuteObject(act);
            }
            catch (Exception ex)
            {
                if (ErrorsEvent != null)
                    ErrorsEvent(_types, new ErrorEventArgs(ex));

                throw new LazyException(ex.Message, ex);
            }
        }

        #endregion

    }
}
